#pragma once

#include <iostream>
#include <fstream>
#include <filesystem>
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sstream>
#include <memory>
#include "Mutex.hpp"

using namespace MutexNamespace;

namespace LogModule
{
    // 每个日志之间的分隔符
    const std::string gsep = "\r\n";
    // 日志基类
    class LogStrategy
    {
    public:
        LogStrategy() = default;
        // 日志储存到指定的文件当中
        virtual void Synclog(const std::string &message) = 0;
    };

    // 将日志储存到显示器文件当中
    class ConsoleLogStrategy : public LogStrategy
    {
    public:
        ConsoleLogStrategy()
        {
        }
        ~ConsoleLogStrategy()
        {
        }
        void Synclog(const std::string &message) override
        {
            // 避免出现不一致问题对当前的写入操作进行加锁
            LockGuard lock(_mutex);
            std::cout << message << gsep;
        }

    private:
        Mutex _mutex;
    };

    // 写入到文件当中默认写入的文件
    const std::string defaultfile = "my.log";
    const std::string defaultpath = "./log";

    class FileLogstrategy : public LogStrategy
    {
    public:
        FileLogstrategy(const std::string &path = defaultpath, const std::string &file = defaultfile)
            : _path(path),
              _file(file)
        {
            // 多当前的操作进行加锁
            LockGuard lock(_mutex);
            // 判断当前的目录是否存在
            if (std::filesystem::exists(_path))
            {
                return;
            }
            // 不存在的时候就创建对应的目录
            try
            {
                std::filesystem::create_directories(_path);
            }
            catch (const std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << "\n";
            }
        }

        ~FileLogstrategy()
        {
        }

        // 进行日志的写入
        void Synclog(const std::string &message) override
        {
            LockGuard lock(_mutex);
            // 将文件的路径调整为./log/my.log的形式
            std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file;
            // 以追加的方式打开对应的日志文件
            std::ofstream f(filename, std::ios::app);
            if (!f.is_open())
            {
                return;
            }
            // 将日志的写入到文件当中
            f << message << "gsep";
            f.close();
        }

    private:
        // 日志文件名
        std::string _file;
        // 日志文件路径
        std::string _path;
        // 锁
        Mutex _mutex;
    };

    // 日志等级
    enum class LogLevel
    {
        DEBUG,   // 调试
        INFO,    // 信息
        WARNING, // 警告
        ERROR,   // 错误
        FATAL    // 致命错误

    };

    // 将对应的日志等级转换为对应的字符串
    std::string Leveltostr(LogLevel level)
    {
        switch (level)
        {
        case LogLevel::DEBUG:
            return "DEBUG";
        case LogLevel::ERROR:
            return "ERROR";
        case LogLevel::FATAL:
            return "FATAL";
        case LogLevel::INFO:
            return "INFO";
        case LogLevel::WARNING:
            return "WARNING";
        default:
            return "NONE";
        }
    }

    // 获取当前的时间戳，并且装换为指定的格式 2025-10-04 14:38:16
    std::string GetTime()
    {
        time_t cur = time(nullptr);
        // 格式化时间戳的结构体tm
        struct tm cur_tm;
        // 进行转换
        localtime_r(&cur, &cur_tm);
        char timebuffer[128];
        // 将时间格式化输出
        snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d",
                 cur_tm.tm_year + 1900,
                 cur_tm.tm_mon + 1,
                 cur_tm.tm_mday,
                 cur_tm.tm_hour,
                 cur_tm.tm_min,
                 cur_tm.tm_sec);
        return timebuffer;
    }

    // 日志类
    class Logger
    {
    public:
        // 默认是将日志写入到显示器文件当中
        Logger()
        {
            EnableConsoleLogStategy();
        }

        // 将日志写入到指定的文件当中
        void EnableFileLogStategy()
        {
            _fflush_strategy = std::make_unique<FileLogstrategy>();
        }

        // 将日志写入到显示器文件当中
        void EnableConsoleLogStategy()
        {
            _fflush_strategy = std::make_unique<ConsoleLogStrategy>();
        }

        // 日志消息类
        class LogMessage
        {

        public:
            // 日志消息构造函数，将[可读性很好的时间] [⽇志等级] [进程pid] [打印对应⽇志的⽂件名][⾏号] -写入到——loginfo字符串当中
            LogMessage(LogLevel &level, std::string &src_name, int line_number, Logger &logger)
                : _curr_time(GetTime()),
                  _level(level),
                  _pid(getpid()),
                  _src_name(src_name),
                  _line_number(line_number),
                  _logger(logger)
            {
                std::stringstream ss;
                ss << "[" << _curr_time << "]"
                   << "[" << Leveltostr(_level) << "]"
                   << "[" << _pid << "]"
                   << "[" << _src_name << "]"
                   << "[" << _line_number << "]"
                   << "- ";
                _loginfo = ss.str();
            }

            // 日志消息当中的<<运算符重载函数
            template <typename T>
            LogMessage &operator<<(const T &info)
            {
                // 将info字符串内的内容追加到_loginfo内
                std::stringstream ss;
                ss << info;
                _loginfo += ss.str();
                return *this;
            }

            // 当Logmessage析构的时候将_loginfo写入到对应的文件当中
            ~LogMessage()
            {
                if (_logger._fflush_strategy)
                {
                    _logger._fflush_strategy->Synclog(_loginfo);
                }
            }

        private:
            // 日志发生时间
            std::string _curr_time;
            // 日志等级
            LogLevel _level;
            // 进程pid
            pid_t _pid;
            // 发生所处的文件名
            std::string _src_name;
            // 文件的行号
            int _line_number;

            // 保存日志的字符串
            std::string _loginfo;
            Logger &_logger;
        };

        // 日志类当中的仿函数
        LogMessage operator()(LogLevel level, std::string name, int line)
        {
            // 将日志的类型、发生的文件名、文件的行号等传给文件消息对象
            return LogMessage(level, name, line, *this);
        }

    private:
        // 日志策略
        std::unique_ptr<LogStrategy> _fflush_strategy;
    };

    Logger logger;

#define LOG(level) logger(level, __FILE__, __LINE__)
#define Enable_Console_log_Strategy() logger.EnableConsoleLogStategy()
#define Enable_File_log_Strategy() logger.EnableFileLogStategy()

}